package org.monitor.servlet.router;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.monitor.servlet.controller.Controller;

import java.util.Set;

public class Routers {
	
	private static final Object SLASH = "/";
	
	private Map<String, Class<? extends Controller>> routerMap;
	private ActionMappings actionMappings;
	private Set<String> excludedMethodNames;

	public Routers(){
		routerMap = new LinkedHashMap<String, Class<? extends Controller>>();
		actionMappings = new ActionMappings();
		excludedMethodNames = buildExcludedMethodNames();
	}
	
	public Routers add(String router, Class<? extends Controller> cls){
		if(!router.startsWith("/")){
			throw new RuntimeException("router [" + router + "] must start with \"/\"!");
		}
		routerMap.put(router, cls);
		
		return this;
	}

	private Set<String> buildExcludedMethodNames() {
		Set<String> excludedMethodName = new HashSet<String>();
		Method[] methods = Controller.class.getMethods();
		for (Method m : methods) {
			if (m.getParameterTypes().length == 0)
				excludedMethodName.add(m.getName());
		}
		return excludedMethodName;
	}
	
	public ActionMappings build(){
		for (Entry<String, Class<? extends Controller>> entry : routerMap.entrySet()) {
			String controllerKey = entry.getKey();
			Class<? extends Controller> cls = entry.getValue();
			
			buildAction(controllerKey, cls);
		}
		return actionMappings;
	}

	private void buildAction(String controllerKey, Class<? extends Controller> cls) {
		boolean sonOfController = (cls.getSuperclass() == Controller.class);
		Method[] methods = (sonOfController ? cls.getDeclaredMethods() : cls.getMethods());
		for (Method method : methods) {
			String methodName = method.getName();
			if (excludedMethodNames.contains(methodName) || method.getParameterTypes().length != 0){
				continue;
			}
			if (sonOfController && !Modifier.isPublic(method.getModifiers())){
				continue;
			}
			
			String actionKey = null;
			if (methodName.equals("index")) {
				actionKey = controllerKey;
			}
			else {
				actionKey = controllerKey.equals(SLASH) ? SLASH + methodName : controllerKey + SLASH + methodName;
			}
			
			actionMappings.addAction(actionKey, new Action(controllerKey, actionKey, cls, method));
		}
	}
}
